package com.company.project.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public final class CommonUtils {

    //日期类型格式
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    /**
     * 判断对象是否为null 或空字符串
     *
     * @param obj
     * @return
     */
    public static boolean isNull(Object obj) {
        if (obj == null)
            return true;
        else if (obj.toString().trim().equals(""))
            return true;
        else
            return false;
    }

    public static boolean objCheckIsNull(Object object) {
        if (isNull(object)) {
            return true;
        }
        Class clazz = (Class) object.getClass(); // 得到类对象
        Field fields[] = clazz.getDeclaredFields(); // 得到所有属性
        boolean flag = true; // 定义返回结果，默认为true
        for (Field field : fields) {
            field.setAccessible(true);
            Object fieldValue = null;
            try {
                fieldValue = field.get(object); // 得到属性值
                Type fieldType = field.getGenericType();// 得到属性类型
                String fieldName = field.getName(); // 得到属性名
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if (fieldValue != null) { // 只要有一个属性值不为null 就返回false 表示对象不为null
                flag = false;
                break;
            }
        }
        return flag;
    }

    /**
     * 判断对象是否为null 或空字符串
     *
     * @param obj
     * @return
     */
    public static boolean isNotNull(Object obj) {
        if (obj == null)
            return false;
        else if (obj.toString().trim().equals(""))
            return false;
        else
            return true;
    }

    /**
     * 将字符串格式yyyy-MM-dd HH:FF:SS的字符串转为日期，格式"yyyy-MM-dd"
     *
     * @param date 日期字符串
     * @return 返回格式化的日期
     * @throws ParseException 分析时意外地出现了错误异常
     */
    public static String strDateFormat(String date) {
        if (date.contains(":")) {
            return date.split(" ")[0];
        } else if (date.length() == 8) {
            return date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6, 8);
        } else
            return date;

    }

    /**
     * 将字符串格式yyyyMMdd的字符串转为日期，格式"yyyy-MM-dd"
     *
     * @param date 日期字符串
     * @return 返回格式化的日期
     * @throws ParseException 分析时意外地出现了错误异常
     */
    public static String strToDateFormat(String date) throws ParseException {
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
            formatter.setLenient(false);
            Date newDate = formatter.parse(date);
            formatter = new SimpleDateFormat(DATE_FORMAT);
            return formatter.format(newDate);
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 将长时间格式字符串转换为时间 yyyy-MM-dd HH:mm:ss
     *
     * @param strDate
     * @return
     */
    public static Date strToDateLong(String strDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    /**
     * 将字符串格式yyyyMMdd的字符串转为日期，格式"yyyy-MM-dd"
     *
     * @param date 日期字符串
     * @return 返回格式化的日期
     * @throws ParseException 分析时意外地出现了错误异常
     */
    public static String strToSimpleDateFormat(String date) throws ParseException {
        try {
            date = date.replace("T", " ");
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            formatter.setLenient(false);
            Date newDate = formatter.parse(date);
            formatter = new SimpleDateFormat(DATE_FORMAT);
            return formatter.format(newDate);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 将字符串格式yyyyMMdd的字符串转为日期，格式"yyyy-MM-dd"
     *
     * @param date 日期字符串
     * @return 返回格式化的日期
     * @throws ParseException 分析时意外地出现了错误异常
     */
    public static String strToDateTimeFormat(String date) throws ParseException {
        try {
            date = date.replace("T", " ");
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            formatter.setLenient(false);
            Date newDate = formatter.parse(date);
            formatter = new SimpleDateFormat(DATETIME_FORMAT);
            return formatter.format(newDate);
        } catch (Exception e) {
        }
        return null;
    }

    public static String DateFormatter(Date date, String formatter) {
        SimpleDateFormat format = new SimpleDateFormat(formatter);
        return format.format(date);
    }


    public static String stampToDate(String s) {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long lt = new Long(s);
        Date date = new Date(lt);
        res = simpleDateFormat.format(date);
        return res;
    }

    public static String stampToDateStr(String s) {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        long lt = new Long(s);
        Date date = new Date(lt);
        res = simpleDateFormat.format(date);
        return res;
    }

    public static String stampToTimeStr(String s) {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
        long lt = new Long(s);
        Date date = new Date(lt);
        res = simpleDateFormat.format(date);
        return res;
    }

    public static <T> List<T> parseJsonArray(String json, Class<T> c) {
        try {
            return JSON.parseArray(json, c);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 去除字符串中的空格、回车、换行符、制表符
     *
     * @param str
     * @return
     */
    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;

    }

    /**
     * 加密字符串SHA加密
     *
     * @param txt
     * @return
     */
    public static String encrypt(String txt) {
        if (txt == null || txt.equals("")) {
            return "";
        }
        String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8",
                "9", "a", "b", "c", "d", "e", "f"};
        byte[] bts;
        StringBuffer sb = new StringBuffer();
        try {
            MessageDigest md = MessageDigest.getInstance("SHA");
            bts = md.digest(txt.getBytes());
            for (int i = 0; i < bts.length; i++) {

                int n = bts[i];
                if (n < 0)
                    n = 256 + n;
                int d1 = n / 16;
                int d2 = n % 16;
                sb.append(hexDigits[d1] + hexDigits[d2]);
            }

        } catch (NoSuchAlgorithmException e) {
            System.out.println(e);
        }
        return sb.toString();
    }

    public static String toJsonString(Object obj) {
        String json = null;
        try {
            ObjectMapper mapper = new ObjectMapper();
            json = mapper.writeValueAsString(obj);
        } catch (JsonGenerationException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return json;
    }

    /**
     * 生成datagrid的需要格式 可以杜绝Long、Integer类型的默认赋值问题
     *
     * @param totalCount
     * @param obj
     * @return
     */
    public static JSONObject toJsonString(int totalCount, Object obj) {
        if (null == obj) {
            JSONObject jsonResult = new JSONObject();
            jsonResult.put("total", totalCount);
            jsonResult.put("rows", new JSONArray());
            return jsonResult;
        }
        if (!Collection.class.isAssignableFrom(obj.getClass())) {
            JSONObject jsonResult = new JSONObject();
            jsonResult.put("total", totalCount);
            jsonResult.put("rows", new JSONArray());
            return jsonResult;
        }
        String json = toJsonString(obj);
        JSONObject jsonResult = new JSONObject();
        jsonResult.put("total", totalCount);
        jsonResult.put("rows", json);
        return jsonResult;
    }

    public static Object toGridJson(int totalCount, Object obj) {
        Map map = new HashMap();
        map.put("total", totalCount);
        map.put("rows", obj);
        return map;
    }


    private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";

    /**
     * 加密
     *
     * @param content    加密的字符串
     * @param encryptKey key值
     * @return
     * @throws Exception
     */
    public static String AESEncrypt(String content, String encryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
        byte[] b = cipher.doFinal(content.getBytes("utf-8"));
        // 采用base64算法进行转码,避免出现中文乱码
        return org.apache.commons.codec.binary.Base64.encodeBase64String(b);

    }

    /**
     * 解密
     *
     * @param encryptStr 解密的字符串
     * @param decryptKey 解密的key值
     * @return
     * @throws Exception
     */
    public static String AESDecrypt(String encryptStr, String decryptKey) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128);
        Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
        // 采用base64算法进行转码,避免出现中文乱码

        byte[] encryptBytes = org.apache.commons.codec.binary.Base64.decodeBase64(encryptStr);
        byte[] decryptBytes = cipher.doFinal(encryptBytes);
        return new String(decryptBytes);
    }

    /**
     * * 该方法是用于相同对象不同属性值的合并<br>
     * * 如果两个相同对象中同一属性都有值，那么sourceBean中的值会覆盖tagetBean重点的值<br>
     * * 如果sourceBean有值，targetBean没有，则采用sourceBean的值<br>
     * * 如果sourceBean没有值，targetBean有，则保留targetBean的值
     *
     * @param sourceBean 被提取的对象bean
     * @param targetBean 用于合并的对象bean
     * @return targetBean, 合并后的对象
     */
    public static <T> T combineSydwCore(T sourceBean, T targetBean) {
        Class<? extends Object> sourceBeanClass = sourceBean.getClass();
        Class<? extends Object> targetBeanClass = targetBean.getClass();

        Field[] sourceFields = sourceBeanClass.getDeclaredFields();
        Field[] targetFields = targetBeanClass.getDeclaredFields();
        for (int i = 0; i < sourceFields.length; i++) {
            Field sourceField = sourceFields[i];
            if (Modifier.isStatic(sourceField.getModifiers())) {
                continue;
            }
            Field targetField = targetFields[i];
            if (Modifier.isStatic(targetField.getModifiers())) {
                continue;
            }
            sourceField.setAccessible(true);
            targetField.setAccessible(true);
            try {
                if (!(sourceField.get(sourceBean) == null) && !"serialVersionUID".equals(sourceField.getName().toString())) {
                    targetField.set(targetBean, sourceField.get(sourceBean));
                }
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return targetBean;
    }

    /**
     * @param request
     * @return String
     * @author HuaZai
     * @contact who.seek.me@java98k.vip
     * @title getUserIP
     * <ul>
     * @description 获取用户真实的IP地址
     * </ul>
     * @createdTime 2017年12月30日 下午6:42:17
     * @version : V1.0.0
     */
    public static String getUserIP(HttpServletRequest request) {
        // 优先取 X-Real-IP
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("x-forwarded-for");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if ("0:0:0:0:0:0:0:1".equals(ip)) {
                ip = "0:0:0:0:0:0:0:1";
            }
        }
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = "0:0:0:0:0:0:0:1";
            return ip;
        }
        int index = ip.indexOf(',');
        if (index >= 0) {
            ip = ip.substring(0, index);
        }

        return ip;
    }

    public static Map<String, Object> obj2Map(Object obj) {
        Map<String, Object> map = new HashMap<String, Object>();
        Field[] fields = obj.getClass().getDeclaredFields(); // 获取对象对应类中的所有属性域
        for (int i = 0; i < fields.length; i++) {
            String varName = fields[i].getName();
            varName = varName.toUpperCase();///将key置为大写，默认为对象的属性
            boolean accessFlag = fields[i].isAccessible(); // 获取原来的访问控制权限
            fields[i].setAccessible(true);// 修改访问控制权限
            try {
                Object object = fields[i].get(obj); // 获取在对象中属性fields[i]对应的对象中的变量
                if (object != null) {
                    map.put(varName, object);
                } else {
                    map.put(varName, null);
                }
                fields[i].setAccessible(accessFlag);// 恢复访问控制权限
            } catch (IllegalArgumentException | IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return map;
    }

    public static <T> List<T> castList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<T>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }

    /**
     * 对象相同属性copy
     *
     * @param obj
     * @param toResult
     * @return
     * @throws Exception 转换报错
     */
    public static <T> T cloneObj(Object obj, Class<T> toResult) {
        if (obj == null) {
            return null;
        }
        try {
            T t = toResult.newInstance();
            Field[] fields = toResult.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);//修改访问权限
                if (Modifier.isFinal(field.getModifiers()))
                    continue;
                if (isWrapType(field)) {
                    String firstLetter = field.getName().substring(0, 1).toUpperCase(); // 首字母大写
                    String getMethodName = "get" + firstLetter + field.getName().substring(1);
                    String setMethodName = "set" + firstLetter + field.getName().substring(1);
                    Method getMethod = obj.getClass().getMethod(getMethodName);   //从源对象获取get方法
                    Method setMethod = toResult.getMethod(setMethodName, new Class[]{field.getType()}); //从目标对象获取set方法

                    //如果get 和 set方法都从一个对象中获取会出现object is not an instance of declaring class这个错误
                    //like: User{name} People{name}
                    //因为如果从源对象中获取，在setMethod.invoke调用的时候，虽然名字相同，会由于类的不同，导致
                    //调用的方法却不是目标对象中应有的方法。实际就是:getMethod = com.package.User.getName();setMethod = com.package.User.setName();
                    //而setMethod.invoke调用的结果就变成 People.setName() == People.(com.package.User.setName())
                    //这里的setName却不是People该有的，so 报错了
                    //同理,如果从目标对象中获取，在getMethod.invoke调用的时候也会出错。
                    //因此，对于getMethod和setMethod的获取应该根据源对象和目标对象区别对待。

                    //当然如果只是进行单独的对象复制，就不用担心会出现调用不属于本身的方法，也就不用区分对象get和set

                    Object value = getMethod.invoke(obj); // get 获取的是源对象的值
                    setMethod.invoke(t, new Object[]{value}); // set 设置的是目标对象的值
                }
            }
            return t;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 是否是基本类型、包装类型、String类型
     */
    private static boolean isWrapType(Field field) {
        String[] types = {"java.lang.Integer", "java.lang.Double", "java.lang.Float", "java.lang.Long",
                "java.lang.Short", "java.lang.Byte", "java.lang.Boolean", "java.lang.Char", "java.lang.String", "int",
                "double", "long", "short", "byte", "boolean", "char", "float"};
        List<String> typeList = Arrays.asList(types);
        return typeList.contains(field.getType().getName()) ? true : false;
    }


    /**
     * 根据身份证号提取生日
     *
     * @return
     */
    public static String getBirthDayByIdCard(String idCard) {
        try {
            return idCard.substring(6, 10) + "-" + idCard.substring(10, 12) + "-" + idCard.substring(12, 14);
        } catch (Exception e) {
            return null;
        }
    }

    public static String getRandomNumber(int len) {
        String source = "0123456789";
        Random r = new Random();
        StringBuilder rs = new StringBuilder();
        for (int j = 0; j < len; j++) {
            rs.append(source.charAt(r.nextInt(10)));
        }
        return rs.toString();
    }

    public static Long getNowTimestamp() {
        return System.currentTimeMillis();
    }

    public static String generateOrderNo(String prefix) {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String dateStr = df.format(new Date());
        return prefix + dateStr + getRandomNumber(8);
    }

    public static String getCurrentDateStr() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return df.format(new Date());
    }


    public static String getCurrentSimpleDateStr() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        return df.format(new Date());
    }

    public static String getNextMonthDate() {
        LocalDateTime plusTime = LocalDateTime.now().plusDays(10);
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return df.format(plusTime);
    }

    public static String getPlusDate(long i) {
        LocalDateTime plusTime = LocalDateTime.now().plusDays(i);
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return df.format(plusTime);
    }


    public static Date stamp2DateTime(String s) {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        long lt = new Long(s);
        Date date = new Date(lt);
        return date;
    }

    public static Date stamp2Date(String s) {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        long lt = new Long(s);
        Date date = new Date(lt);
        return date;
    }

    public static int getWeekDayByPlusDate(long i) {
        LocalDateTime plusTime = LocalDateTime.now().plusDays(i);
        return  plusTime.getDayOfWeek().getValue();
    }


    public static Integer convertMin2Ms(String min) {
        return Integer.valueOf(min) * 60 * 1000;
    }

    public static Integer convertMin2Ms(Integer min) {
        return min * 60 * 1000;
    }

    public static Integer convertHour2Ms(String hour) {
        return Integer.valueOf(hour) * 60 * 60 * 1000;
    }

    public static Integer convertMs2Min(Integer ms) {
        return ms / 60 / 1000;
    }

    /**
     * 提取500的错误信息
     *
     * @param target
     * @param content
     * @return
     */
    public static String extractErrorMessage(String target, String content) {
        try {
            String str1 = content.substring(0, content.indexOf(target));
            String str2 = content.substring(str1.length() + target.length());
            JSONObject json = JSONObject.parseObject(str2);
            return json.getString("message");
        } catch (Exception e) {
            log.error(e.getMessage());
            return "医院接口内部错误";
        }
    }

    /**
     * 将处方列表的item_class 转换为：只包含药品、只包含检查检验、都包含
     *
     * @param itemList
     * @return
     */
    public static String getPrescType(String itemList) {
        if (itemList.indexOf("0") == -1 && itemList.indexOf("1") == -1) {
            return "2";
        } else if (itemList.indexOf("2") == -1 && itemList.indexOf("3") == -1) {
            return "1";
        } else {
            return "3";
        }
    }

    /**
     * 通过生日计算年龄
     *
     * @param birthday
     * @return
     */
    public static int getAgeByBirth(Date birthday) {
        int age = 0;
        try {
            Calendar now = Calendar.getInstance();
            now.setTime(new Date());

            Calendar birth = Calendar.getInstance();
            birth.setTime(birthday);

            if (birth.after(now)) {
                age = 0;
            } else {
                age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR);
                if (now.get(Calendar.DAY_OF_YEAR) > birth.get(Calendar.DAY_OF_YEAR)) {
                    age += 1;
                }
            }
            return age;
        } catch (Exception e) {
            return 0;
        }
    }
    /**
     * 获取随机数
     * @param len
     * @return
     */
    public static String getRandom(int len) {
        int digit = (int) Math.pow(10, len - 1);
        int rs = new Random().nextInt(digit * 10);
        if (rs < digit) {
            rs += digit;
        }
        return String.valueOf(rs);
    }
    /**
     * 获取精确到秒的时间戳
     * @param date
     * @return
     */
    public static int getSecondTimestampTwo(Date date){
        if (null == date) {
            return 0;
        }
        String timestamp = String.valueOf(date.getTime()/1000);
        return Integer.valueOf(timestamp);
    }


    public static void main(String[] args) {

//        System.out.println(generateOrderNo("1122"));
//        String content = "status 500 reading HisServiceFeign\n" +
//                "ClientInterface#GetInptList(GetInptListReq); content:\n" +
//                "{\"timestamp\":\"2020-08-21T03:23:31.785+0000\",\"status\":500,\"error\":\"Internal Server Error\",\"message\":\"Unrecognized field \\\"Ward\\\" (class com.easyaccess\n" +
//                ".core.model.hisApi.Inpatient.GetInptListRep), not marked as ignorable (18 known properties: \\\"InpatientNo\\\", \\\"BedNo\\\", \\\"WardName\\\", \\\"CardType\\\", \\\n" +
//                "\"Reasons\\\", \\\"PatientName\\\", \\\"CardNo\\\", \\\"Status\\\", \\\"InDate\\\", \\\"WardId\\\", \\\"Tips\\\", \\\"OutDate\\\", \\\"Deposit\\\", \\\"DoctorName\\\", \\\"DeptName\\\", \\\"Inda\n" +
//                "y\\\", \\\"Balance\\\", \\\"TotalAmount\\\"])\\n at [Source: (StringReader); line: 11, column: 26] (through reference chain: com.easyaccess.core.model.hisApi.Un\n" +
//                "iformRepParam[\\\"Output\\\"]->java.util.ArrayList[0]->com.easyaccess.core.model.hisApi.Inpatient.GetInptListRep[\\\"Ward\\\"])\",\"path\":\"/his/Inpatient/GetIn\n" +
//                "ptList\"}";
//        String target = "content:";
//
//        String text = "-3#休息休息多多多多";
//        String[] status = text.split("\\#");
//        System.out.println(status[0]+"===="+status[1]);
//        System.out.println(encrypt("12345678"));
//        extractErrorMessage(target, content);
        System.out.println(getWeekDayByPlusDate(2));
    }

}
